﻿using Google.Protobuf;
using Grpc.Core;
using MX.Core.Assets;
using MX.Core.Extensions;
using MX.Core.Log;
using MX.Core.Webp;
using MX.Proto;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UserCenter.Api.CallContexts;
using UserCenter.Api.Core;
using UserCenter.BLL;
using UserCenter.Grpc;
using UserCenter.Models;
using GrpcLoginStatus = UserCenter.Grpc.LoginStatus;


namespace UserCenter.Api.Tools
{
    public static class Utils
    {
        static ILogger logger = Logger.GetLogger("Utils");
        const int ECH = 0x9;


        public static void Throw(this ServerCallContext context, int layer, int detail)
        {
            context.Throw((layer > 9 ? layer * 100 : layer * 1000) + detail);
        }
        /// <summary>
        /// 抛出GRPC异常
        /// </summary>
        /// <param name="context"></param>
        /// <param name="code"></param>
        /// <param name="rpcValue"></param>
        public static void Throw(this ServerCallContext context, int code, string rpcValue = "server error")
        {
            //TODO 如果连续grpc级别的错误将视为攻击,需要断开
            var errorMessage = new ErrorMessage
            {
                Code = code,
                Message = ErrorCodeHelper.Get(code)
            };
            context.ResponseTrailers.Add("error", Convert.ToBase64String(errorMessage.ToByteArray()));
            throw new RpcException(new Status(StatusCode.Unknown, rpcValue));
        }


        /// <summary>
        /// 获取客户端IP
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public static string ClientIp(this ServerCallContext context)
        {
            return context.Peer.Split(':')[1];
        }


        /// <summary>
        /// 获取商户Id
        /// </summary>
        /// <param name="cxt"></param>
        /// <returns></returns>
        public static int MerchantId(this ServerCallContext cxt)
        {

            if (cxt is BaseMerchantContext)
            {
                return (cxt as BaseMerchantContext).MerchantId;
            }
            //if (cxt is MerchantApiContext)
            //{
            //    return (cxt as MerchantApiContext).MerchantId;
            //}
            return 0;
        }


        public static long MemberId(this ServerCallContext cxt)
        {

            if (cxt is MemberApiContext)
            {
                return (cxt as MemberApiContext).MemberId;
            }
            return 0;
        }



        public static int CheckMerchantHeader(ServerCallContext context, Func<int, string> getSignFun)
        {
            int outTimeSeconds = 60;
            long timestamp = 0;
            var timestampEntry = context.RequestHeaders.SingleOrDefault((entry) => entry.Key == "timestamp");
            if (timestampEntry != null)
            {
                string timestampStr = timestampEntry.Value;
                if (!string.IsNullOrEmpty(timestampStr))
                {
                    if (long.TryParse(timestampStr, out timestamp))
                    {
                        DateTime timestampTime = timestamp.FromTimestamp();
                        if ((DateTime.Now - timestampTime).TotalSeconds > outTimeSeconds)
                        {
                            //请求超时请重试
                            context.Throw(ECH, 03);
                        }
                    }
                    else
                    {
                        //Timestamp不为long
                        context.Throw(ECH, 01);
                    }
                }
                else
                {
                    //缺少Timestamp头信息
                    context.Throw(ECH, 00);
                }
            }
            else
            {
                context.Throw(ECH, 00);
            }
            string sign = "";
            var signEntry = context.RequestHeaders.SingleOrDefault((entry) => entry.Key == "sign");
            if (signEntry != null)
            {
                sign = signEntry.Value;
                if (string.IsNullOrEmpty(sign))
                {
                    //缺少Sign头信息
                    context.Throw(ECH, 08);
                }
            }
            else
            {
                //缺少Sign头信息
                context.Throw(ECH, 08);
            }
            var merIdEntry = context.RequestHeaders.SingleOrDefault((entry) => entry.Key == "merchantid");
            if (merIdEntry != null)
            {
                string merIdStr = merIdEntry.Value;
                if (!string.IsNullOrEmpty(merIdStr))
                {
                    if (int.TryParse(merIdStr, out int merId))
                    {
                        var signKey = getSignFun(merId);
                        string signValue = $"{timestamp}{signKey}".MD5().MD5();
                        if (!sign.Equals(signValue, StringComparison.InvariantCultureIgnoreCase))
                        {
                            context.Throw(ECH, 09);
                        }
                        else
                        {
                            return merId;
                        }
                    }
                    else
                    {
                        //MerchantId不为Int
                        context.Throw(ECH, 10);
                    }
                }
                else
                {
                    context.Throw(ECH, 07);
                }
            }
            else
            {
                context.Throw(ECH, 07);
            }
            return 0;
        }

        public static JToken IsValidJson(string strInput)
        {
            strInput = strInput.Trim();
            if (strInput.StartsWith("{") && strInput.EndsWith("}"))
            {
                try
                {
                    var obj = JToken.Parse(strInput);
                    return obj;
                }
                catch (Exception ex) //some other exception
                {
                    logger.Error(ex, "");
                    return null;
                }
            }
            else
            {
                return null;
            }
        }




        public static string UploadBase64Image(ServerCallContext context, byte[] avaterData, bool throwErr)
        { 
            if (avaterData == null || avaterData.Length < 10)
            {
                if (throwErr)
                    context.Throw(ECH, 0x2);
                return null;
            }
            try
            {
                using (MemoryStream stream = new MemoryStream(avaterData))
                {
                    using (Bitmap sourceImage = new Bitmap(stream))
                    {
                        if (!WebpUtils.FromImage(sourceImage, out avaterData))
                        {
                            avaterData = null;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                logger.Error(ex, "");
            }
            if (avaterData == null)
            {
                if (throwErr)
                    context.Throw(ECH, 0x3);
                return null;
            }
            var uploadResult = FileUploader.Upload(ApiConfig.Default.ResHost, "ico.webp", avaterData, "UserCenter");
            if (uploadResult.code != ReponseCode.SUCCESSED)
            {
                if (throwErr)
                    context.Throw(ECH, 0x4);
                return null;
            }
            else
            {
                return uploadResult.url;
            }
        }

        public static GrpcLoginStatus ToGrpcLoginStatus(this Member.Status status)
        {
            return (GrpcLoginStatus)Enum.Parse(typeof(GrpcLoginStatus), status.ToString());
        }

        public static bool CheckByteEnum<T>(uint value, out T? enumValue) where T : struct
        {
            enumValue = null;
            if (value >= byte.MinValue && value <= byte.MaxValue)
            {
                var byteValue = Convert.ToByte(value);
                if (Enum.IsDefined(typeof(T), byteValue))
                {
                    enumValue = (T)Enum.ToObject(typeof(T), byteValue);
                    return true;
                }
            }
            return false;
        }

    }
}
